//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package webhook_serv_gitee

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"atomgit.com/openlinksaas/api-server/dao/event_dao"
	"atomgit.com/openlinksaas/proto-gen-go.git/events_api"
	gitee_hook "atomgit.com/openlinksaas/webhook.git/gitee"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"google.golang.org/protobuf/types/known/anypb"
)

var sourceType zapcore.Field

func init() {
	sourceType = zap.String("sourceType", "gitee")
}

func Process(projectId, eventSourceId string, header http.Header, payload []byte, secret string, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	token := header.Get("X-Gitee-Token")
	timeStampStr := header.Get("X-Gitee-Timestamp")
	//check time
	timeStamp, err := strconv.ParseInt(timeStampStr, 10, 64)
	if err != nil {
		return nil, nil, err
	}
	timeDiff := time.Now().UnixNano()/1e6 - timeStamp
	if timeDiff > 600*1000 { //10分钟
		logger.Warn("skip too old webhook event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, fmt.Errorf("too old event")
	}
	//check token
	if !(token == secret || gitee_hook.GenToken(timeStamp, secret) == token) {
		logger.Error("wrong webhook token", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, fmt.Errorf("wrong event token")
	}
	eventStr := header.Get("X-Gitee-Event")
	eventStr = strings.ToLower(eventStr)
	if strings.Contains(eventStr, "issue") {
		return processIssue(projectId, eventSourceId, payload, timeStamp, logger)
	} else if strings.Contains(eventStr, "request") {
		return processPullRequest(projectId, eventSourceId, payload, timeStamp, logger)
	} else if strings.Contains(eventStr, "note") {
		return processNote(projectId, eventSourceId, payload, timeStamp, logger)
	} else if strings.Contains(eventStr, "push") || strings.Contains(eventStr, "tag") {
		return processPush(projectId, eventSourceId, payload, timeStamp, logger)
	} else {
		logger.Error("wrong webhook event type", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, fmt.Errorf("unkwown event data")
	}
}

func processIssue(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive issue event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	issueHook := &gitee_hook.IssueHook{}
	err := json.Unmarshal(payload, issueHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := issueHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := ""
	sourceDisplayName := ""
	if issueHook.Sender != nil {
		if issueHook.Sender.Login != nil {
			sourceUserName = *issueHook.Sender.Login
			sourceDisplayName = issueHook.Sender.Name
		}
	}
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITEE),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processPullRequest(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive pull event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	pullRequestHook := &gitee_hook.PullRequestHook{}
	err := json.Unmarshal(payload, pullRequestHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	pullRequestEvent := pullRequestHook.ToProtoBuf()
	eventAny, err := anypb.New(pullRequestEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := ""
	sourceDisplayName := ""
	if pullRequestHook.Sender != nil {
		if pullRequestHook.Sender.Login != nil {
			sourceUserName = *pullRequestHook.Sender.Login
			sourceDisplayName = pullRequestHook.Sender.Name
		}
	}
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITEE),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}
func processNote(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive note event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	noteHook := &gitee_hook.NoteHook{}
	err := json.Unmarshal(payload, noteHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	noteEvent := noteHook.ToProtoBuf()
	eventAny, err := anypb.New(noteEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := ""
	sourceDisplayName := ""
	if noteHook.Sender != nil {
		if noteHook.Sender.Login != nil {
			sourceUserName = *noteHook.Sender.Login
			sourceDisplayName = noteHook.Sender.Name
		}
	}
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITEE),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processPush(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive push event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	pushHook := &gitee_hook.PushHook{}
	err := json.Unmarshal(payload, pushHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	pushEvent := pushHook.ToProtoBuf()
	eventAny, err := anypb.New(pushEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := ""
	sourceDisplayName := ""
	if pushHook.Sender != nil {
		if pushHook.Sender.Login != nil {
			sourceUserName = *pushHook.Sender.Login
			sourceDisplayName = pushHook.Sender.Name
		}
	}
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITEE),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}
